import axios, { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'
import axiosRetry from 'axios-retry'
import jsonpAdapter from 'axios-jsonp'
import { useUserStore } from '@/store/useStore'
import router from '@/router/index'
import { addReqCache, getKey, reqCacheAdapter } from '@/utils/reqCache'

// axios封装功能
// 1.重复请求拦截,保持请求唯一
// 2.请求失败重试 axios-retry
// 3.API版本全局控制 v1> v2>v3
// 4.jsonp支持
// 5.token 无感刷新 在响应拦截器进行刷新， 多个请求等待token刷新之后，再逐个重新发送
// https://blog.csdn.net/jieyucx/article/details/131951952
// 6.请求竞争处理
// 7.接口缓存处理
// https://zhuanlan.zhihu.com/p/691234963
// https://juejin.cn/post/7053471988752318472?searchId=2024053118090084919E73C2640E1D7725

// api版本控制
const version = 'v1'
// 环境变量
let env: Record<string, unknown> = {}
if (import.meta.env) {
  env = import.meta.env || {}
}
// 重复请求拦截,保持请求唯一
// 正在发送的请求的abort控制器对象
const reqings = {}
// 请求拦截白名单
const whiteListReqAbort = ['/noInterpretor', '/getUser']

// root url + version
export const baseURL = (env.VITE_BASE_URL || '') + `/${version}`
const Instance: AxiosInstance = axios.create({
  baseURL,
  timeout: 1000 * 10,
  // 跨域携带cookie
  withCredentials: true,
  adapter: reqCacheAdapter,
})
// 失败重试，也支持单个请求设置重试属性
axiosRetry(Instance, {
  // 重试次数
  retries: 3,
  // 重试间隔
  retryDelay: () => 1000,
  shouldResetTimeout: true,
  // 重试条件设置：http异常、网络异常、404
  retryCondition: (err) => axiosRetry.isNetworkOrIdempotentRequestError(err) || err.response?.status === 404,
})
/**
 * 扩充RequestConfig参数
 * isUnque 是否请求唯一，去掉重复请求 默认true，是
 * isCache 是否请求缓存 默认false 否
 * cacheMaxage 请求缓存时长 默认5 * 1000
 */
interface InternalReqConfig extends InternalAxiosRequestConfig {
  // 是否请求唯一，去掉重复请求
  isUnque?: boolean
  isCache?: boolean
  cacheMaxage?: number
}
interface InternalResConfig extends InternalAxiosRequestConfig {
  isUnque?: boolean
  isCache?: boolean
  cacheMaxage?: number
}
interface reqConfig extends AxiosRequestConfig {
  isUnque?: boolean
  isCache?: boolean
  cacheMaxage?: number
}

// 请求拦截器
Instance.interceptors.request.use(
  (config: InternalReqConfig) => {
    const userStore = useUserStore()
    if (userStore.accessToken) {
      config.headers.Authorization = `Bearer ${userStore.accessToken}`
    }

    // 控制版本
    // config.baseURL = config.baseURL.replace('{version}', version)

    // 请求缓存
    getKey(config)

    uniqueBeforeRequest(config)

    return config
  },
  (error) => {
    // 对请求错误做处理
    return Promise.reject(error)
  }
)
Instance.interceptors.response.use(
  (response) => {
    // 对响应数据做点什么 响应码在2xx范围内
    const config = response.config
    // 添加请求缓存
    addReqCache(config, response)
    // 请求完成，清除唯一队列
    uniqueBeforeResponse(config)
    return response
  },
  (error) => {
    // 对响应错误处理 错误码超出2xx
    // 请求唯一标识
    // console.log('error', error)
    const config = error.config
    const response = error.response
    // 被abort的req, 已经在abort时 从abortReqs中去掉了
    if (axios.isCancel(error)) {
      Promise.reject({ message: '重复请求，自动拦截并取消' })
    } else {
      // 请求完成，清除唯一队列
      uniqueBeforeResponse(config)
      resErrorHandler(response)
    }

    return Promise.reject(error)
  }
)
// token是否在刷新中
let isRefreshing: boolean = false
// 刷新token时 积压的请求
const refreshCacheReqArr = new Set()
// 响应错误处理
const resErrorHandler = (response) => {
  // 403token过期或者校验不通过
  const data = response?.data
  const status = response?.status
  const userStore = useUserStore()
  // const code = data?.code
  let msg = ''
  switch (status) {
    // 未登录 或没有token
    case 401:
      msg = data.message
      userStore.accessToken = ''
      router.push({ path: '/login', query: { redirect: response.config?.url } })
      break
    // token无效 或 过期
    case 403:
      msg = data.message
      userStore.accessToken = ''
      // 此处需要刷新token，检查refreshToken
      if (!isRefreshing) {
        isRefreshing = true
        request({
          url: '/refresh',
          method: 'POST',
          data: {
            refreshToken: userStore.refreshToken,
          },
        })
          .then((res) => {
            const { data, code } = res
            if (code === 200) {
              userStore.accessToken = data
              // 清空一下之前token过期积压的请求
              refreshCacheReqArr?.forEach((item) => {
                item && item(data)
              })
            }
          })
          .catch((error) => {
            console.log('refresh error', error)
            const refreshRes = error?.response
            if (refreshRes.status === 403) {
              // 长token失效
              refreshCacheReqArr.clear()
              const curpath = router.currentRoute.value.fullPath
              userStore.refreshToken = ''
              router.push({ path: '/login', query: { redirect: curpath } })
            }
          })
          .finally(() => {
            isRefreshing = false
          })
      }
      // refresh刷新token时 将其他请求缓存起来
      return new Promise((resolve) => {
        const refreshCacheReqFun = (newToken) => {
          console.log('重新执行了', newToken)
          // response.config.headers.Authorization = `Bearer ${newToken}`
          // 执行之后将自己移除
          refreshCacheReqArr.delete(refreshCacheReqFun)
          // 执行
          resolve(request(response.config))
        }
        refreshCacheReqArr.add(refreshCacheReqFun)
      })

    default:
      msg = data?.message
  }
  console.log('resErrorHandler:', msg)
}

// 保持请求唯一unique，取消重复请求
// 请求前：请求拦截
const uniqueBeforeRequest = (config: InternalReqConfig) => {
  // 请求唯一标识
  const reqKey = `${config.method}&${config.url}`
  if (config?.isUnque) {
    // 请求前 若是重复请求，则中断掉
    if (reqings[reqKey]) {
      abortReq(reqKey)
    }
    // 生成取消请求控制器
    const controller = new AbortController()
    config.signal = controller.signal
    // 不在白名单的，才会被加入中断监控之中
    if (whiteListReqAbort.indexOf(config.url) === -1) {
      reqings[reqKey] = controller
    }
  }
}
// 请求后：响应拦截
const uniqueBeforeResponse = (config: InternalResConfig) => {
  if (config?.isUnque) {
    // 请求唯一标识
    const reqKey = `${config?.method}&${config?.url}`
    // 请求完成 取消监控
    abortReqFinish(reqKey)
  }
}
// 取消请求，删除监控
const abortReq = (reqKey, isAbort = true) => {
  if (reqings[reqKey] as AbortController) {
    isAbort && reqings[reqKey]?.abort()
    reqings[reqKey] = null
  }
}
// 请求完成，删除监控
const abortReqFinish = (reqKey) => {
  if (reqings[reqKey] as AbortController) {
    reqings[reqKey] = null
  }
}
// 封装成函数形式
export async function request(config: reqConfig) {
  try {
    const response = await Instance({
      isUnque: true,
      ...config,
    })
    return response?.data
  } catch (error) {
    return Promise.reject(error)
  }
}
// 支持jsonp跨域请求  能在promise then函数中拿到数据 无需调用callback
export function jsonp(config?: AxiosRequestConfig) {
  const url = baseURL + config.url
  // config.headers.Authorization = 'Bearer ' + useUserStore().accessToken
  return Instance({
    ...config,
    url: url,
    adapter: jsonpAdapter,
    method: 'GET',

    params: {
      type: 'jsonp',
      tk: 'Bearer ' + useUserStore().accessToken,
    },
    // 可自定义回调函数key 无需管
    // callbackParamName: 'cb'
  })
}

export default Instance
